// package ed25519密码套件，主要用于P2P节点
package ed25519

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"log"
	"os"
	"osiris/logger"

	"github.com/libp2p/go-libp2p/core/crypto"
)

const (
	privKeyFileName = "privkey.txt"
)

// var 节点密钥。模块初始化时已经根据私钥文件赋值，若之后的命令行有指定新的密钥，则覆盖
//
//	@param peerPubKey ed25519公钥
//	@param peerPrivKey ed25519私钥
var (
	peerPubKey     crypto.PubKey
	peerPrivKey    crypto.PrivKey
	peerPubKeyStr  string
	peerPrivKeyStr string
)

// init 生成兜底的公私钥对：从文件里加载ECC私钥并生成对应的公钥，没有就随机生成一个新的私钥并写入文件，并根据私钥生成公钥；后续命令行有指定私钥的话，会覆盖掉从文件读来的或者随机生成的公私钥
func init() {
	var errorOccured = true
	defer func() {
		if !errorOccured {
			return
		}

		//ECC 生成公私钥对
		var err error
		peerPrivKey, peerPubKey, err = generateECCKeyPair()
		if err != nil {
			logger.Error(map[string]interface{}{"[ocrypto] [Init] Generate Key Pair": err})
			log.Fatalln(err)
		}

		peerPrivKeyStr, _ = PrivKeyToStr(peerPrivKey)
		peerPubKeyStr, _ = PubKeyToStr(peerPubKey)

		go func() {
			savePrivKey(peerPrivKey)
		}()
	}()

	//从私钥文件里读私钥
	var err1 error
	peerPrivKeyStr, err1 = readPrivKeyStr()
	if err1 != nil {
		return
	}
	var err2 error
	peerPrivKey, err2 = StrToPrivKey(peerPrivKeyStr)
	if err2 != nil {
		return
	}

	//根据私钥生成公钥
	peerPubKey = peerPrivKey.GetPublic()
	var err3 error
	peerPubKeyStr, err3 = PubKeyToStr(peerPubKey)
	if err3 != nil {
		return
	}

	errorOccured = false
}

// InitKeyPair （在这之前ocrypto模块初始化已经通过读密钥文件或者随机生成了一个兜底的公私钥对）初始化节点的ECC键值对，若指定的私钥有效，则覆盖当前公私钥对
//
//	@param privKeyStr 私钥字符串（ECC+Base64）
//	@return bool 是否覆盖了当前的公私钥对
func InitKeyPair(tempPrivKeyStr string) bool {
	var tempPrivKey crypto.PrivKey
	var tempPubkey crypto.PubKey
	var tempPubKeyStr string
	errorOccured := true

	//全程没错误发生的话覆盖当前的公私钥对
	defer func() {
		if errorOccured {
			return
		}

		peerPubKey = tempPubkey
		peerPrivKey = tempPrivKey
		peerPubKeyStr = tempPubKeyStr
		peerPrivKeyStr = tempPrivKeyStr
	}()

	if tempPrivKeyStr == "" || tempPrivKeyStr == peerPrivKeyStr {
		return false
	}

	//Base64字符串转crypto.PrivKey
	var err1 error
	tempPrivKey, err1 = StrToPrivKey(tempPrivKeyStr)
	if err1 != nil {
		logger.Println("Invalid privKeyStr!")
		logger.Error(map[string]interface{}{"[ocrypto] [Init Key pair] str to privKey": err1})
		return false
	}

	//获得ECC私钥对应的公钥
	tempPubkey = tempPrivKey.GetPublic()

	//公钥转公钥字符串
	var err2 error
	tempPubKeyStr, err2 = PubKeyToStr(tempPubkey)
	if err2 != nil {
		logger.Println("Invalid pubKeyStr!")
		logger.Error(map[string]interface{}{"[ocrypto] [Init Key pair] pubkey to str": err2})
		return false
	}

	errorOccured = false
	return true
}

func generateECCKeyPair() (crypto.PrivKey, crypto.PubKey, error) {
	return crypto.GenerateKeyPair(crypto.Ed25519, 0)
}

func readPrivKeyStr() (string, error) {
	file, err := os.Open(privKeyFileName)
	if err != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Read PrivKey Str] open file " + privKeyFileName: err})
		return "", err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)

	// 读取第一行字符串
	if scanner.Scan() {
		line1 := scanner.Text()
		logger.Info(map[string]interface{}{"[ocrypto] [Read PrivKey Str] scan privkey": line1})
		return line1, nil
	}

	return "", nil
}

func savePrivKey(privKey crypto.PrivKey) error {
	file, err1 := os.Create(privKeyFileName)
	if err1 != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Save PrivKey] create file " + privKeyFileName: err1})
		return err1
	}
	defer file.Close()

	privKeyStr, err2 := PrivKeyToStr(privKey)
	if err2 != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Save PrivKey] create file " + privKeyFileName: err2})
		return err2
	}

	// 写入第一行字符串
	_, err3 := fmt.Fprintln(file, privKeyStr)
	if err3 != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Save PrivKey] create file " + privKeyFileName: err3})
		return err3
	}

	return nil
}

// PrivKeyToStr ed25519私钥转字符串
//
//	@param privKey
//	@return string
//	@return error
func PrivKeyToStr(privKey crypto.PrivKey) (string, error) {

	privKeyBytes, err := crypto.MarshalPrivateKey(privKey)
	if err != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [PrivKey To Str] marshal privKey": err})
		return "", err
	}

	// 将字节切片转换为十六进制字符串
	privKeyString := hex.EncodeToString(privKeyBytes)
	return privKeyString, nil
}

// StrToPrivKey 字符串转ed25519私钥
//
//	@param privKeyStr
//	@return crypto.PrivKey
//	@return error
func StrToPrivKey(privKeyStr string) (crypto.PrivKey, error) {
	privKeyBytes, err := hex.DecodeString(privKeyStr)
	if err != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Str To PrivKey] decode privKeyStr": err})
		return nil, err
	}

	// 从字节切片中恢复私钥
	privKey, err := crypto.UnmarshalPrivateKey(privKeyBytes)
	if err != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Str To PrivKey] unmarshal privKeyStr": err})
		return nil, err
	}

	return privKey, nil
}

// PubKeyToStr ed25519公钥转字符串
//
//	@param pubKey
//	@return string
//	@return error
func PubKeyToStr(pubKey crypto.PubKey) (string, error) {
	pubKeyBytes, err := crypto.MarshalPublicKey(pubKey)
	if err != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [PubKey To Str] marshal pubKey": err})
		return "", err
	}

	// 将字节切片转换为十六进制字符串
	pubKeyString := hex.EncodeToString(pubKeyBytes)
	return pubKeyString, nil
}

// StrToPubKey 字符串转ed25519公钥
//
//	@param pubKeyStr
//	@return crypto.PubKey
//	@return error
func StrToPubKey(pubKeyStr string) (crypto.PubKey, error) {
	pubKeyBytes, err := hex.DecodeString(pubKeyStr)
	if err != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Str To PubKey] decode pubKeyStr": err})
		return nil, err
	}

	// 从字节切片中恢复私钥
	pubKey, err := crypto.UnmarshalPublicKey(pubKeyBytes)
	if err != nil {
		logger.Error(map[string]interface{}{"[ocrypto] [Str To PubKey] unmarshal pubKeyStr": err})
		return nil, err
	}

	return pubKey, nil
}

func GetPeerKeyPair() (crypto.PubKey, crypto.PrivKey) {
	return peerPubKey, peerPrivKey
}

func GetPeerPrivKey() crypto.PrivKey {
	return peerPrivKey
}

func GetPeerPubKey() crypto.PubKey {
	return peerPubKey
}

func GetPeerPrivKeyStr() string {
	return peerPrivKeyStr
}

func GetPeerPubKeyStr() string {
	return peerPubKeyStr
}
